package com.common.frame.utils


import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Base64
import com.common.frame.utils.CloseUtil.closeIO
import java.io.*
import java.security.DigestInputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import kotlin.experimental.and


object FileUtil {
    private val LINE_SEP = System.getProperty("line.separator")
    private val MIME_MapTable = arrayOf(
        // {后缀名，MIME类型}
        arrayOf(".3gp", "video/3gpp"),
        arrayOf(".apk", "application/vnd.android.package-archive"),
        arrayOf(".asf", "video/x-ms-asf"),
        arrayOf(".avi", "video/x-msvideo"),
        arrayOf(".bin", "application/octet-stream"),
        arrayOf(".bmp", "image/bmp"),
        arrayOf(".c", "text/plain"),
        arrayOf(".class", "application/octet-stream"),
        arrayOf(".conf", "text/plain"),
        arrayOf(".cpp", "text/plain"),
        arrayOf(".doc", "application/msword"),
        arrayOf(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
        arrayOf(".xls", "application/vnd.ms-excel"),
        arrayOf(".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
        arrayOf(".exe", "application/octet-stream"),
        arrayOf(".gif", "image/gif"),
        arrayOf(".gtar", "application/x-gtar"),
        arrayOf(".gz", "application/x-gzip"),
        arrayOf(".h", "text/plain"),
        arrayOf(".htm", "text/html"),
        arrayOf(".html", "text/html"),
        arrayOf(".jar", "application/java-archive"),
        arrayOf(".java", "text/plain"),
        arrayOf(".jpeg", "image/jpeg"),
        arrayOf(".jpg", "image/jpeg"),
        arrayOf(".js", "application/x-javascript"),
        arrayOf(".log", "text/plain"),
        arrayOf(".m3u", "audio/x-mpegurl"),
        arrayOf(".m4a", "audio/mp4a-latm"),
        arrayOf(".m4b", "audio/mp4a-latm"),
        arrayOf(".m4p", "audio/mp4a-latm"),
        arrayOf(".m4u", "video/vnd.mpegurl"),
        arrayOf(".m4v", "video/x-m4v"),
        arrayOf(".mov", "video/quicktime"),
        arrayOf(".mp2", "audio/x-mpeg"),
        arrayOf(".mp3", "audio/x-mpeg"),
        arrayOf(".mp4", "video/mp4"),
        arrayOf(".mpc", "application/vnd.mpohun.certificate"),
        arrayOf(".mpe", "video/mpeg"),
        arrayOf(".mpeg", "video/mpeg"),
        arrayOf(".mpg", "video/mpeg"),
        arrayOf(".mpg4", "video/mp4"),
        arrayOf(".mpga", "audio/mpeg"),
        arrayOf(".msg", "application/vnd.ms-outlook"),
        arrayOf(".ogg", "audio/ogg"),
        arrayOf(".pdf", "application/pdf"),
        arrayOf(".png", "image/png"),
        arrayOf(".pps", "application/vnd.ms-powerpoint"),
        arrayOf(".ppt", "application/vnd.ms-powerpoint"),
        arrayOf(
            ".pptx",
            "application/vnd.openxmlformats-officedocument.presentationml.presentation"
        ),
        arrayOf(".prop", "text/plain"),
        arrayOf(".rc", "text/plain"),
        arrayOf(".rmvb", "audio/x-pn-realaudio"),
        arrayOf(".rtf", "application/rtf"),
        arrayOf(".sh", "text/plain"),
        arrayOf(".tar", "application/x-tar"),
        arrayOf(".tgz", "application/x-compressed"),
        arrayOf(".txt", "text/plain"),
        arrayOf(".wav", "audio/x-wav"),
        arrayOf(".wma", "audio/x-ms-wma"),
        arrayOf(".wmv", "audio/x-ms-wmv"),
        arrayOf(".wps", "application/vnd.ms-works"),
        arrayOf(".xml", "text/plain"),
        arrayOf(".z", "application/x-compress"),
        arrayOf(".zip", "application/x-zip-compressed"),
        arrayOf("", "*/*")
    )

    /**
     * 根据文件路径获取文件

     * @param filePath 文件路径
     * *
     * @return 文件
     */
    fun getFileByPath(filePath: String): File? {
        return if (isSpace(filePath)) null else File(filePath)
    }

    /**
     * 判断文件是否存在

     * @param filePath 文件路径
     * *
     * @return `true`: 存在<br></br>`false`: 不存在
     */
    fun isFileExists(filePath: String): Boolean {
        return isFileExists(getFileByPath(filePath))
    }

    /**
     * 判断文件是否存在

     * @param file 文件
     * *
     * @return `true`: 存在<br></br>`false`: 不存在
     */
    fun isFileExists(file: File?): Boolean {
        return file != null && file.exists()
    }

    /**
     * 重命名文件

     * @param filePath 文件路径
     * *
     * @param newName  新名称
     * *
     * @return `true`: 重命名成功<br></br>`false`: 重命名失败
     */
    fun rename(filePath: String, newName: String): Boolean {
        return rename(getFileByPath(filePath), newName)
    }

    /**
     * 重命名文件

     * @param file    文件
     * *
     * @param newName 新名称
     * *
     * @return `true`: 重命名成功<br></br>`false`: 重命名失败
     */
    fun rename(file: File?, newName: String): Boolean {
        // 文件为空返回false
        if (file == null) return false
        // 文件不存在返回false
        if (!file.exists()) return false
        // 新的文件名为空返回false
        if (isSpace(newName)) return false
        // 如果文件名没有改变返回true
        if (newName == file.name) return true
        val newFile = File(file.parent + File.separator + newName)
        // 如果重命名的文件已存在返回false
        return !newFile.exists() && file.renameTo(newFile)
    }

    /**
     * 判断是否是目录

     * @param dirPath 目录路径
     * *
     * @return `true`: 是<br></br>`false`: 否
     */
    fun isDir(dirPath: String): Boolean {
        return isDir(getFileByPath(dirPath)!!)
    }

    /**
     * 判断是否是目录

     * @param file 文件
     * *
     * @return `true`: 是<br></br>`false`: 否
     */
    fun isDir(file: File): Boolean {
        return isFileExists(file) && file.isDirectory
    }

    /**
     * 判断是否是文件

     * @param filePath 文件路径
     * *
     * @return `true`: 是<br></br>`false`: 否
     */
    fun isFile(filePath: String): Boolean {
        return isFile(getFileByPath(filePath)!!)
    }

    /**
     * 判断是否是文件

     * @param file 文件
     * *
     * @return `true`: 是<br></br>`false`: 否
     */
    fun isFile(file: File): Boolean {
        return isFileExists(file) && file.isFile
    }

    /**
     * 判断目录是否存在，不存在则判断是否创建成功

     * @param dirPath 目录路径
     * *
     * @return `true`: 存在或创建成功<br></br>`false`: 不存在或创建失败
     */
    fun createOrExistsDir(dirPath: String): Boolean {
        return createOrExistsDir(getFileByPath(dirPath))
    }

    /**
     * 判断目录是否存在，不存在则判断是否创建成功

     * @param file 文件
     * *
     * @return `true`: 存在或创建成功<br></br>`false`: 不存在或创建失败
     */
    fun createOrExistsDir(file: File?): Boolean {
        // 如果存在，是目录则返回true，是文件则返回false，不存在则返回是否创建成功
        return file != null && if (file.exists()) file.isDirectory else file.mkdirs()
    }

    /**
     * 判断文件是否存在，不存在则判断是否创建成功

     * @param filePath 文件路径
     * *
     * @return `true`: 存在或创建成功<br></br>`false`: 不存在或创建失败
     */
    fun createOrExistsFile(filePath: String): Boolean {
        return createOrExistsFile(getFileByPath(filePath))
    }

    /**
     * 判断文件是否存在，不存在则判断是否创建成功

     * @param file 文件
     * *
     * @return `true`: 存在或创建成功<br></br>`false`: 不存在或创建失败
     */
    fun createOrExistsFile(file: File?): Boolean {
        if (file == null) return false
        // 如果存在，是文件则返回true，是目录则返回false
        if (file.exists()) return file.isFile
        if (!createOrExistsDir(file.parentFile)) return false
        return try {
            file.createNewFile()
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }

    }

    /**
     * 判断文件是否存在，存在则在创建之前删除

     * @param file 文件
     * *
     * @return `true`: 创建成功<br></br>`false`: 创建失败
     */
    fun createFileByDeleteOldFile(file: File?): Boolean {
        if (file == null) return false
        // 文件存在并且删除失败返回false
        if (file.exists() && !file.delete()) return false
        // 创建目录失败返回false
        if (!createOrExistsDir(file.parentFile)) return false
        return try {
            file.createNewFile()
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }

    }

    /**
     * 判断文件是否存在，存在则在创建之前删除

     * @param dir 文件
     * *
     * @return `true`: 创建成功<br></br>`false`: 创建失败
     */
    fun createFileByDeleteOldFile(dirPath: String): Boolean {
        val file = getFileByPath(dirPath)
        return if (createOrExistsDir(file)) {
            deleteFilesInDir(file)
            true
        } else {
            false
        }
    }

    fun createDirByDeleteOldFile(file: File?): Boolean {
        return if (createOrExistsDir(file)) {
            deleteFilesInDir(file)
            true
        } else {
            false
        }
    }

    /**
     * 复制或移动目录

     * @param srcDirPath  源目录路径
     * *
     * @param destDirPath 目标目录路径
     * *
     * @param isMove      是否移动
     * *
     * @return `true`: 复制或移动成功<br></br>`false`: 复制或移动失败
     */
    private fun copyOrMoveDir(srcDirPath: String, destDirPath: String, isMove: Boolean): Boolean {
        return copyOrMoveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), isMove)
    }

    /**
     * 复制或移动目录

     * @param srcDir  源目录
     * *
     * @param destDir 目标目录
     * *
     * @param isMove  是否移动
     * *
     * @return `true`: 复制或移动成功<br></br>`false`: 复制或移动失败
     */
    private fun copyOrMoveDir(srcDir: File?, destDir: File?, isMove: Boolean): Boolean {
        if (srcDir == null || destDir == null) return false
        // 如果目标目录在源目录中则返回false，看不懂的话好好想想递归怎么结束
        // srcPath : F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res
        // destPath: F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res1
        // 为防止以上这种情况出现出现误判，须分别在后面加个路径分隔符
        val srcPath = srcDir.path + File.separator
        val destPath = destDir.path + File.separator
        if (destPath.contains(srcPath)) return false
        // 源文件不存在或者不是目录则返回false
        if (!srcDir.exists() || !srcDir.isDirectory) return false
        // 目标目录不存在返回false
        if (!createOrExistsDir(destDir)) return false
        val files = srcDir.listFiles()
        for (file in files) {
            val oneDestFile = File(destPath + file.name)
            if (file.isFile) {
                // 如果操作失败返回false
                if (!copyOrMoveFile(file, oneDestFile, isMove)) return false
            } else if (file.isDirectory) {
                // 如果操作失败返回false
                if (!copyOrMoveDir(file, oneDestFile, isMove)) return false
            }
        }
        return !isMove || deleteDir(srcDir)
    }

    /**
     * 复制或移动文件

     * @param srcFilePath  源文件路径
     * *
     * @param destFilePath 目标文件路径
     * *
     * @param isMove       是否移动
     * *
     * @return `true`: 复制或移动成功<br></br>`false`: 复制或移动失败
     */
    private fun copyOrMoveFile(
        srcFilePath: String,
        destFilePath: String,
        isMove: Boolean
    ): Boolean {
        return copyOrMoveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), isMove)
    }

    /**
     * 复制或移动文件

     * @param srcFile  源文件
     * *
     * @param destFile 目标文件
     * *
     * @param isMove   是否移动
     * *
     * @return `true`: 复制或移动成功<br></br>`false`: 复制或移动失败
     */
    private fun copyOrMoveFile(srcFile: File?, destFile: File?, isMove: Boolean): Boolean {
        if (srcFile == null || destFile == null) return false
        // 源文件不存在或者不是文件则返回false
        if (!srcFile.exists() || !srcFile.isFile) return false
        // 目标文件存在且是文件则返回false
        if (destFile.exists() && destFile.isFile) return false
        // 目标目录不存在返回false
        if (!createOrExistsDir(destFile.parentFile)) return false
        return try {
            FileIOUtil.writeFileFromIS(
                destFile,
                FileInputStream(srcFile),
                false
            ) && !(isMove && !deleteFile(srcFile))
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
            false
        }

    }

    /**
     * 复制目录

     * @param srcDirPath  源目录路径
     * *
     * @param destDirPath 目标目录路径
     * *
     * @return `true`: 复制成功<br></br>`false`: 复制失败
     */
    fun copyDir(srcDirPath: String, destDirPath: String): Boolean {
        return copyDir(getFileByPath(srcDirPath)!!, getFileByPath(destDirPath)!!)
    }

    /**
     * 复制目录

     * @param srcDir  源目录
     * *
     * @param destDir 目标目录
     * *
     * @return `true`: 复制成功<br></br>`false`: 复制失败
     */
    fun copyDir(srcDir: File, destDir: File): Boolean {
        return copyOrMoveDir(srcDir, destDir, false)
    }

    /**
     * 复制文件

     * @param srcFilePath  源文件路径
     * *
     * @param destFilePath 目标文件路径
     * *
     * @return `true`: 复制成功<br></br>`false`: 复制失败
     */
    fun copyFile(srcFilePath: String, destFilePath: String): Boolean {
        return copyFile(getFileByPath(srcFilePath)!!, getFileByPath(destFilePath)!!)
    }

    /**
     * 复制文件

     * @param srcFile  源文件
     * *
     * @param destFile 目标文件
     * *
     * @return `true`: 复制成功<br></br>`false`: 复制失败
     */
    fun copyFile(srcFile: File, destFile: File): Boolean {
        return copyOrMoveFile(srcFile, destFile, false)
    }

    /**
     * 移动目录

     * @param srcDirPath  源目录路径
     * *
     * @param destDirPath 目标目录路径
     * *
     * @return `true`: 移动成功<br></br>`false`: 移动失败
     */
    fun moveDir(srcDirPath: String, destDirPath: String): Boolean {
        return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath))
    }

    /**
     * 移动目录

     * @param srcDir  源目录
     * *
     * @param destDir 目标目录
     * *
     * @return `true`: 移动成功<br></br>`false`: 移动失败
     */
    fun moveDir(srcDir: File?, destDir: File?): Boolean {
        return copyOrMoveDir(srcDir, destDir, true)
    }

    /**
     * 移动文件

     * @param srcFilePath  源文件路径
     * *
     * @param destFilePath 目标文件路径
     * *
     * @return `true`: 移动成功<br></br>`false`: 移动失败
     */
    fun moveFile(srcFilePath: String, destFilePath: String): Boolean {
        return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath))
    }

    /**
     * 移动文件

     * @param srcFile  源文件
     * *
     * @param destFile 目标文件
     * *
     * @return `true`: 移动成功<br></br>`false`: 移动失败
     */
    fun moveFile(srcFile: File?, destFile: File?): Boolean {
        return copyOrMoveFile(srcFile, destFile, true)
    }

    /**
     * 删除目录

     * @param dirPath 目录路径
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteDir(dirPath: String): Boolean {
        return deleteDir(getFileByPath(dirPath))
    }

    /**
     * 删除目录

     * @param dir 目录
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteDir(dir: File?): Boolean {
        if (dir == null) return false
        // 目录不存在返回true
        if (!dir.exists()) return true
        // 不是目录返回false
        if (!dir.isDirectory) return false
        // 现在文件存在且是文件夹
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                if (file.isFile) {
                    if (!deleteFile(file)) return false
                } else if (file.isDirectory) {
                    if (!deleteDir(file)) return false
                }
            }
        }
        return dir.delete()
    }

    /**
     * 删除文件

     * @param srcFilePath 文件路径
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteFile(srcFilePath: String): Boolean {
        return deleteFile(getFileByPath(srcFilePath))
    }

    /**
     * 删除文件

     * @param file 文件
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteFile(file: File?): Boolean {
        return file != null && (!file.exists() || file.isFile && file.delete())
    }

    /**
     * 删除目录下的所有文件

     * @param dirPath 目录路径
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteFilesInDir(dirPath: String): Boolean {
        return deleteFilesInDir(getFileByPath(dirPath))
    }

    /**
     * 删除目录下的所有文件

     * @param dir 目录
     * *
     * @return `true`: 删除成功<br></br>`false`: 删除失败
     */
    fun deleteFilesInDir(dir: File?): Boolean {
        if (dir == null) return false
        // 目录不存在返回true
        if (!dir.exists()) return true
        // 不是目录返回false
        if (!dir.isDirectory) return false
        // 现在文件存在且是文件夹
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                if (file.isFile) {
                    if (!deleteFile(file)) return false
                } else if (file.isDirectory) {
                    if (!deleteDir(file)) return false
                }
            }
        }
        return true
    }

    fun dirFileSize(dir: File?): Int {
        val files = dir?.listFiles()
        return if (files.isNullOrEmpty()) 0 else files.size
    }

    /**
     * 获取目录下所有文件

     * @param dirPath     目录路径
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDir(dirPath: String, isRecursive: Boolean): List<File>? {
        return listFilesInDir(getFileByPath(dirPath)!!, isRecursive)
    }

    /**
     * 获取目录下所有文件

     * @param dir         目录
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDir(dir: File, isRecursive: Boolean): List<File>? {
        if (!isDir(dir)) return null
        if (isRecursive) return listFilesInDir(dir)
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            Collections.addAll(list, *files)
        }
        return list
    }

    /**
     * 获取目录下所有文件包括子目录

     * @param dirPath 目录路径
     * *
     * @return 文件链表
     */
    fun listFilesInDir(dirPath: String): List<File>? {
        return listFilesInDir(getFileByPath(dirPath)!!)
    }

    /**
     * 获取目录下所有文件包括子目录

     * @param dir 目录
     * *
     * @return 文件链表
     */
    fun listFilesInDir(dir: File): List<File>? {
        if (!isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                list.add(file)
                if (file.isDirectory) {
                    val fileList = listFilesInDir(file)
                    if (fileList != null) {
                        list.addAll(fileList)
                    }
                }
            }
        }
        return list
    }

    /**
     * 获取目录下所有后缀名为suffix的文件
     *
     * 大小写忽略

     * @param dirPath     目录路径
     * *
     * @param suffix      后缀名
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(
        dirPath: String,
        suffix: String,
        isRecursive: Boolean
    ): List<File>? {
        return listFilesInDirWithFilter(getFileByPath(dirPath), suffix, isRecursive)
    }

    /**
     * 获取目录下所有后缀名为suffix的文件
     *
     * 大小写忽略

     * @param dir         目录
     * *
     * @param suffix      后缀名
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(dir: File?, suffix: String, isRecursive: Boolean): List<File>? {
        if (isRecursive) return listFilesInDirWithFilter(dir, suffix)
        if (dir == null || !isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            files.filterTo(list) { it.name.toUpperCase().endsWith(suffix.toUpperCase()) }
        }
        return list
    }

    /**
     * 获取目录下所有后缀名为suffix的文件包括子目录
     *
     * 大小写忽略

     * @param dirPath 目录路径
     * *
     * @param suffix  后缀名
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(dirPath: String, suffix: String): List<File>? {
        return listFilesInDirWithFilter(getFileByPath(dirPath), suffix)
    }

    /**
     * 获取目录下所有后缀名为suffix的文件包括子目录
     *
     * 大小写忽略

     * @param dir    目录
     * *
     * @param suffix 后缀名
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(dir: File?, suffix: String): List<File>? {
        if (dir == null || !isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                if (file.name.toUpperCase().endsWith(suffix.toUpperCase())) {
                    list.add(file)
                }
                if (file.isDirectory) {
                    list.addAll(listFilesInDirWithFilter(file, suffix)!!)
                }
            }
        }
        return list
    }

    /**
     * 获取目录下所有符合filter的文件

     * @param dirPath     目录路径
     * *
     * @param filter      过滤器
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(
        dirPath: String,
        filter: FilenameFilter,
        isRecursive: Boolean
    ): List<File>? {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive)
    }

    /**
     * 获取目录下所有符合filter的文件

     * @param dir         目录
     * *
     * @param filter      过滤器
     * *
     * @param isRecursive 是否递归进子目录
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(
        dir: File?,
        filter: FilenameFilter,
        isRecursive: Boolean
    ): List<File>? {
        if (isRecursive) return listFilesInDirWithFilter(dir, filter)
        if (dir == null || !isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            files.filterTo(list) { filter.accept(it.parentFile, it.name) }
        }
        return list
    }

    /**
     * 获取目录下所有符合filter的文件包括子目录

     * @param dirPath 目录路径
     * *
     * @param filter  过滤器
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(dirPath: String, filter: FilenameFilter): List<File>? {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter)
    }

    /**
     * 获取目录下所有符合filter的文件包括子目录

     * @param dir    目录
     * *
     * @param filter 过滤器
     * *
     * @return 文件链表
     */
    fun listFilesInDirWithFilter(dir: File?, filter: FilenameFilter): List<File>? {
        if (dir == null || !isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                if (filter.accept(file.parentFile, file.name)) {
                    list.add(file)
                }
                if (file.isDirectory) {
                    list.addAll(listFilesInDirWithFilter(file, filter)!!)
                }
            }
        }
        return list
    }

    /**
     * 获取目录下指定文件名的文件包括子目录
     *
     * 大小写忽略

     * @param dirPath  目录路径
     * *
     * @param fileName 文件名
     * *
     * @return 文件链表
     */
    fun searchFileInDir(dirPath: String, fileName: String): List<File>? {
        return searchFileInDir(getFileByPath(dirPath), fileName)
    }

    /**
     * 获取目录下指定文件名的文件包括子目录
     *
     * 大小写忽略

     * @param dir      目录
     * *
     * @param fileName 文件名
     * *
     * @return 文件链表
     */
    fun searchFileInDir(dir: File?, fileName: String): List<File>? {
        if (dir == null || !isDir(dir)) return null
        val list = ArrayList<File>()
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                if (file.name.toUpperCase() == fileName.toUpperCase()) {
                    list.add(file)
                }
                if (file.isDirectory) {
                    list.addAll(searchFileInDir(file, fileName)!!)
                }
            }
        }
        return list
    }

    /**
     * 获取文件最后修改的毫秒时间戳

     * @param filePath 文件路径
     * *
     * @return 文件最后修改的毫秒时间戳
     */
    fun getFileLastModified(filePath: String): Long {
        return getFileLastModified(getFileByPath(filePath))
    }

    /**
     * 获取文件最后修改的毫秒时间戳

     * @param file 文件
     * *
     * @return 文件最后修改的毫秒时间戳
     */
    fun getFileLastModified(file: File?): Long {
        if (file == null) return -1
        return file.lastModified()
    }

    /**
     * 简单获取文件编码格式

     * @param filePath 文件路径
     * *
     * @return 文件编码
     */
    fun getFileCharsetSimple(filePath: String): String? {
        return getFileCharsetSimple(getFileByPath(filePath)!!)
    }

    /**
     * 简单获取文件编码格式

     * @param file 文件
     * *
     * @return 文件编码
     */
    fun getFileCharsetSimple(file: File): String {
        var p = 0
        var `is`: InputStream? = null
        try {
            `is` = BufferedInputStream(FileInputStream(file))
            p = (`is`.read() shl 8) + `is`.read()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            closeIO(`is`)
        }
        return when (p) {
            0xefbb -> "UTF-8"
            0xfffe -> "Unicode"
            0xfeff -> "UTF-16BE"
            else -> "GBK"
        }
    }

    /**
     * 获取文件行数

     * @param filePath 文件路径
     * *
     * @return 文件行数
     */
    fun getFileLines(filePath: String): Int? {
        return getFileLines(getFileByPath(filePath)!!)
    }

    /**
     * 获取文件行数
     *
     * 比readLine要快很多

     * @param file 文件
     * *
     * @return 文件行数
     */
    fun getFileLines(file: File): Int {
        var count = 1
        var `is`: InputStream? = null
        try {
            `is` = BufferedInputStream(FileInputStream(file))
            val buffer = ByteArray(1024)
            var readChars = `is`.read(buffer, 0, 1024)
            if (LINE_SEP!!.endsWith("\n")) {
                while (readChars != -1) {
                    (0 until readChars)
                        .filter { buffer[it] == '\n'.toByte() }
                        .forEach { ++count }
                    readChars = `is`.read(buffer, 0, 1024)
                }
            } else {
                while (readChars != -1) {
                    (0 until readChars)
                        .filter { buffer[it] == '\r'.toByte() }
                        .forEach { ++count }
                    readChars = `is`.read(buffer, 0, 1024)
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            closeIO(`is`!!)
        }
        return count
    }

    /**
     * 获取目录大小

     * @param dirPath 目录路径
     * *
     * @return 文件大小
     */
    fun getDirSize(dirPath: String): String? {
        return getDirSize(getFileByPath(dirPath)!!)
    }

    /**
     * 获取目录大小

     * @param dir 目录
     * *
     * @return 文件大小
     */
    fun getDirSize(dir: File): String {
        val len = getDirLength(dir)
        return if (len == -1L) "" else byte2FitMemorySize(len)
    }

    /**
     * 获取文件大小

     * @param filePath 文件路径
     * *
     * @return 文件大小
     */
    fun getFileSize(filePath: String): String? {
        return getFileSize(getFileByPath(filePath)!!)
    }

    /**
     * 获取文件大小

     * @param file 文件
     * *
     * @return 文件大小
     */
    fun getFileSize(file: File): String {
        val len = getFileLength(file)
        return if (len == -1L) "" else byte2FitMemorySize(len)
    }

    /**
     * 获取目录长度

     * @param dirPath 目录路径
     * *
     * @return 目录长度
     */
    fun getDirLength(dirPath: String): Long {
        return getDirLength(getFileByPath(dirPath)!!)
    }

    /**
     * 获取目录长度

     * @param dir 目录
     * *
     * @return 目录长度
     */
    fun getDirLength(dir: File): Long {
        if (!isDir(dir)) return -1
        var len: Long = 0
        val files = dir.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (file in files) {
                len += if (file.isDirectory) {
                    getDirLength(file)
                } else {
                    file.length()
                }
            }
        }
        return len
    }

    /**
     * 获取文件长度

     * @param filePath 文件路径
     * *
     * @return 文件长度
     */
    fun getFileLength(filePath: String): Long {
        return getFileLength(getFileByPath(filePath)!!)
    }

    /**
     * 获取文件长度

     * @param file 文件
     * *
     * @return 文件长度
     */
    fun getFileLength(file: File): Long {
        if (!isFile(file)) return -1
        return file.length()
    }

    /**
     * 获取文件的MD5校验码

     * @param filePath 文件路径
     * *
     * @return 文件的MD5校验码
     */
    fun getFileMD5ToString(filePath: String): String? {
        val file = if (isSpace(filePath)) null else File(filePath)
        return getFileMD5ToString(file!!)
    }

    /**
     * 获取文件的MD5校验码

     * @param filePath 文件路径
     * *
     * @return 文件的MD5校验码
     */
    fun getFileMD5(filePath: String): ByteArray? {
        val file = if (isSpace(filePath)) null else File(filePath)
        return getFileMD5(file!!)
    }

    /**
     * 获取文件的MD5校验码

     * @param file 文件
     * *
     * @return 文件的MD5校验码
     */
    fun getFileMD5ToString(file: File): String? {
        return bytes2HexString(getFileMD5(file)!!)
    }

    /**
     * 获取文件的MD5校验码

     * @param file 文件
     * *
     * @return 文件的MD5校验码
     */
    fun getFileMD5(file: File?): ByteArray? {
        if (file == null) return null
        var dis: DigestInputStream? = null
        try {
            val fis = FileInputStream(file)
            var md = MessageDigest.getInstance("MD5")
            dis = DigestInputStream(fis, md)
            val buffer = ByteArray(1024 * 256)
            while (true) {
                if (dis.read(buffer) <= 0) break
            }
            md = dis.messageDigest
            return md.digest()
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            closeIO(dis!!)
        }
        return null
    }

    /**
     * 获取全路径中的最长目录

     * @param file 文件
     * *
     * @return filePath最长目录
     */
    fun getDirName(file: File?): String? {
        if (file == null) return null
        return getDirName(file.path)
    }

    /**
     * 获取全路径中的最长目录

     * @param filePath 文件路径
     * *
     * @return filePath最长目录
     */
    fun getDirName(filePath: String): String {
        if (isSpace(filePath)) return filePath
        val lastSep = filePath.lastIndexOf(File.separator)
        return if (lastSep == -1) "" else filePath.substring(0, lastSep + 1)
    }

    /**
     * 获取全路径中的文件名

     * @param file 文件
     * *
     * @return 文件名
     */
    fun getFileName(file: File?): String? {
        if (file == null) return null
        return getFileName(file.path)
    }

    /**
     * 获取全路径中的文件名

     * @param filePath 文件路径
     * *
     * @return 文件名
     */
    fun getFileName(filePath: String): String {
        if (isSpace(filePath)) return filePath
        val lastSep = filePath.lastIndexOf(File.separator)
        return if (lastSep == -1) filePath else filePath.substring(lastSep + 1)
    }

    /**
     * 获取全路径中的不带拓展名的文件名

     * @param file 文件
     * *
     * @return 不带拓展名的文件名
     */
    fun getFileNameNoExtension(file: File?): String? {
        if (file == null) return null
        return getFileNameNoExtension(file.path)
    }

    /**
     * 获取全路径中的不带拓展名的文件名

     * @param filePath 文件路径
     * *
     * @return 不带拓展名的文件名
     */
    fun getFileNameNoExtension(filePath: String): String {
        if (isSpace(filePath)) return filePath
        val lastPoi = filePath.lastIndexOf('.')
        val lastSep = filePath.lastIndexOf(File.separator)
        if (lastSep == -1) {
            return if (lastPoi == -1) filePath else filePath.substring(0, lastPoi)
        }
        if (lastPoi == -1 || lastSep > lastPoi) {
            return filePath.substring(lastSep + 1)
        }
        return filePath.substring(lastSep + 1, lastPoi)
    }

    /**
     * 获取全路径中的文件拓展名

     * @param file 文件
     * *
     * @return 文件拓展名
     */
    fun getFileExtension(file: File?): String? {
        if (file == null) return null
        return getFileExtension(file.path)
    }

    /**
     * 获取全路径中的文件拓展名

     * @param filePath 文件路径
     * *
     * @return 文件拓展名
     */
    fun getFileExtension(filePath: String): String {
        if (isSpace(filePath)) return filePath
        val lastPoi = filePath.lastIndexOf('.')
        val lastSep = filePath.lastIndexOf(File.separator)
        if (lastPoi == -1 || lastSep >= lastPoi) return ""
        return filePath.substring(lastPoi + 1)
    }

    /**
     * 获取全路径中的文件拓展名

     * @param filePath 文件路径
     * *
     * @return 文件拓展名
     */
    fun getFileSuffix(filePath: String): String {
        if (isSpace(filePath)) return filePath
        val lastPoi = filePath.lastIndexOf('.')
        if (lastPoi == -1) return ""
        return filePath.substring(lastPoi)
    }

    ///////////////////////////////////////////////////////////////////////////
    // copy from ConvertUtils
    ///////////////////////////////////////////////////////////////////////////

    private val hexDigits =
        charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

    /**
     * byteArr转hexString
     *
     * 例如：
     * bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns 00A8

     * @param bytes 字节数组
     * *
     * @return 16进制大写字符串
     */
    private fun bytes2HexString(bytes: ByteArray): String? {
        if (bytes.isEmpty()) return null
        val len = bytes.size
        if (len <= 0) return null
        val ret = CharArray(len shl 1)
        var i = 0
        var j = 0
        while (i < len) {
            ret[j++] = hexDigits[(bytes[i].toInt() ushr 4) and 0x0f]
            ret[j++] = hexDigits[(bytes[i] and 0x0f).toInt()]
            i++
        }
        return String(ret)
    }

    /**
     * 字节数转合适内存大小
     *
     * 保留3位小数

     * @param byteNum 字节数
     * *
     * @return 合适内存大小
     */
    @SuppressLint("DefaultLocale")
    private fun byte2FitMemorySize(byteNum: Long): String {
        return when {
            byteNum < 0 -> "shouldn't be less than zero!"
            byteNum < 1024 -> String.format("%.3fB", byteNum.toDouble() + 0.0005)
            byteNum < 1048576 -> String.format("%.3fKB", byteNum.toDouble() / 1024 + 0.0005)
            byteNum < 1073741824 -> String.format("%.3fMB", byteNum.toDouble() / 1048576 + 0.0005)
            else -> String.format("%.3fGB", byteNum.toDouble() / 1073741824 + 0.0005)
        }
    }

    private fun isSpace(s: String?): Boolean {
        if (s == null) return true
        var i = 0
        val len = s.length
        while (i < len) {
            if (!Character.isWhitespace(s[i])) {
                return false
            }
            ++i
        }
        return true
    }


    fun writeFile(bytes: ByteArray, file: File, append: Boolean = true) {
        val fileOutPutStream = FileOutputStream(file, append)
        fileOutPutStream.write(bytes)
        closeIO(fileOutPutStream)
    }

    fun writeFile(bytes: ByteArray, file: String, append: Boolean = true) {
        writeFile(bytes, File(file), append)
    }


    /**
     * 将字符串写入文件
     *
     * @param filePath 文件路径
     * @param content  写入内容
     * @param append   是否追加在文件末
     * @return `true`: 写入成功<br></br>`false`: 写入失败
     */
    fun writeFileFromString(filePath: String, content: String, append: Boolean): Boolean {
        return writeFileFromString(getFileByPath(filePath), content, append)
    }

    /**
     * 将字符串写入文件
     *
     * @param file    文件
     * @param content 写入内容
     * @param append  是否追加在文件末
     * @return `true`: 写入成功<br></br>`false`: 写入失败
     */
    fun writeFileFromString(file: File?, content: String?, append: Boolean): Boolean {
        if (file == null || content == null) return false
        if (!createOrExistsFile(file)) return false
        var fileWriter: FileWriter? = null
        return try {
            fileWriter = FileWriter(file, append)
            fileWriter.write(content)
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        } finally {
            closeIO(fileWriter!!)
        }
    }

//    fun getFileCacheDir(): File? {
//        val cacheDir =
//            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//                MyApp.instance.externalCacheDir
//            } else {
//                MyApp.instance.cacheDir
//            }
//        if (cacheDir != null && cacheDir.exists()) {
//            cacheDir.mkdirs()
//        }
//        return cacheDir
//    }

    /**
     * 将文件转换成byte数组
     * @param filePath
     * @return
     */
    fun File2byte(tradeFile: File?): ByteArray? {
        var buffer: ByteArray? = null
        try {
            val fis = FileInputStream(tradeFile)
            val bos = ByteArrayOutputStream()
            val b = ByteArray(1024)
            var n: Int
            while (fis.read(b).also { n = it } != -1) {
                bos.write(b, 0, n)
            }
            fis.close()
            bos.close()
            buffer = bos.toByteArray()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return buffer
    }

    fun unitConversion(length: Long): String? {
        var txt = ""
        var dvalue = 0.0
        var unit = "byte"
        when {
            length >= 1024 * 1024 * 1024 -> {
                dvalue = length * 1.0 / (1024 * 1024 * 1024)
                unit = "GB"
            }
            length >= 1024 * 1024 -> {
                dvalue = length * 1.0 / (1024 * 1024)
                unit = "MB"
            }
            length >= 1024 -> {
                dvalue = length * 1.0 / 1024
                unit = "KB"
            }
            else -> {
                dvalue = length.toDouble()
            }
        }
        txt = String.format("%3f", dvalue)
        txt = txt.substring(0, 4)
        if (txt[3] == '.') txt = txt.substring(0, 3)
        txt += unit
        return txt
    }

    fun getloToDate(lo: Long): String? {
        val date = Date(lo * 1000)
        return (1900 + date.year).toString() + "-" + (date.month + 1) + "-" + date.day
    }

    fun isDirExits(dir: String): Boolean {
        val file = File(dir)
        return file.exists() && file.isDirectory
    }

    //Delete all files in directory
    fun delAllFile(path: String): Boolean {
        var flag = false
        val file = File(path)
        if (!file.exists()) {
            return flag
        }
        if (!file.isDirectory) {
            return flag
        }
        val tempList = file.list()
        var temp: File? = null
        if (tempList != null) {
            for (i in tempList.indices) {
                temp = if (path.endsWith(File.separator)) {
                    File(path + tempList[i])
                } else {
                    File(path + File.separator + tempList[i])
                }
                if (temp.isFile) {
                    temp.delete()
                    flag = true
                }
                if (temp.isDirectory) {
                    delAllFile(path + "/" + tempList[i]) // 先删除文件夹里面的文件
                    delFolder(path + "/" + tempList[i]) // 再删除空文件夹
                    flag = true
                }
            }
        }
        return flag
    }

    //delete a directory
    fun delFolder(folderPath: String) {
        val folder = File(folderPath)
        if (!folder.exists()) {
            return
        }
        if (!folder.isDirectory) {
            return
        }
        delAllFile(folderPath)
        folder.delete()
    }

    //create a directory
    fun mkDir(dir: String): Boolean {
        val file = File(dir)
        return file.exists() && file.isDirectory || file.mkdir() || file.isDirectory
    }

    //save image to the SD card
    fun saveImage(fileName: String, pageNo: Int, data: ByteArray) {
        val path = "$fileName/$pageNo.jpg"
        saveData(path, data)
    }

    //read image data
    private fun readData(path: String): ByteArray? {
        var buffer: ByteArrayOutputStream? = null
        val file = File(path)
        return if (!file.exists()) {
            null
        } else try {
            val inputstream: InputStream = FileInputStream(file)
            buffer = ByteArrayOutputStream(1024)
            var tmp: ByteArray? = ByteArray(1024)
            var len: Int
            while (inputstream.read(tmp).also { len = it } != -1) {
                buffer.write(tmp, 0, len)
            }
            inputstream.close()
            tmp = null
            buffer.toByteArray()
        } catch (e: IOException) {
            null
        }
    }

    //save image data
    fun saveData(path: String, data: ByteArray) {
        try {
            val file = File(path)
            if (!file.exists()) {
                file.createNewFile()
            }
            val outStream = FileOutputStream(file)
            outStream.write(data)
            outStream.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    fun getImageContentUri(
        context: Context,
        imageFile: File
    ): Uri? {
        val filePath = imageFile.absolutePath
        val cursor = context.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf(MediaStore.Images.Media._ID),
            MediaStore.Images.Media.DATA + "=? ",
            arrayOf(filePath),
            null
        )
        return if (cursor != null && cursor.moveToFirst()) {
            val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
            val baseUri = Uri.parse("content://media/external/images/media")
            Uri.withAppendedPath(baseUri, "" + id)
        } else {
            if (imageFile.exists()) {
                val values = ContentValues()
                values.put(MediaStore.Images.Media.DATA, filePath)
                context.contentResolver
                    .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
            } else {
                null
            }
        }
    }

    fun imageToBase64(path: String?): String? {
        if (TextUtils.isEmpty(path)) {
            return null
        }
        var `is`: InputStream? = null
        val data: ByteArray
        var result: String? = null
        try {
            `is` = FileInputStream(path)
            //创建一个字符流大小的数组。
            data = ByteArray(`is`.available())
            //写入数组
            `is`.read(data)
            //用默认的编码格式进行编码
            result = Base64.encodeToString(data, Base64.DEFAULT)
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            closeIO(`is`)

        }
        return result
    }

    public fun getMIMEType(file: File): String {
        var type = "*/*"
        val fName = file.name

        //获取后缀名前的分隔符"."在fName中的位置。
        val dotIndex = fName.lastIndexOf(".")
        if (dotIndex < 0) {
            return type
        }

        /* 获取文件的后缀名*/
        val end = fName.substring(dotIndex, fName.length).toLowerCase()
        if (TextUtils.isEmpty(end)) {
            return type
        }

        //在MIME和文件类型的匹配表中找到对应的MIME类型。
        for (i in MIME_MapTable.indices) {
            if (end == MIME_MapTable[i][0]) {
                type = MIME_MapTable[i][1]
                break
            }
        }
        return type
    }
}